/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2011 SMUNIX
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Providence M. Salumu <providence.salumu@smunix.com>
 */

#include "test.hpp"
#include <slib/aggregator.hpp>
#include <slib/frame.hpp>

#define LOG_TEST() BOOST_TEST_MESSAGE ("line=" << __LINE__ << ", func=" <<BOOST_CURRENT_FUNCTION)

using namespace smunix;

struct Configuration;
struct Configurable
{
  virtual ~Configurable (void) {}
  virtual void Configure (Configuration const &) = 0;
};
struct Configuration : Injectable<Configuration>
{
  virtual ~Configuration (void) {}
  void SetBuildTime (int v)
  {
    _ = v;
  }
  int GetBuildTime (void) const
  {
    return _;
  }
private:
  int _ = 0;
};

struct Motor : public Configurable
{
  virtual ~Motor (void) {}
  virtual void Configure (Configuration const &cfg)
  {
    BOOST_TEST_MESSAGE ("Motor[0x" << std::hex << this << std::dec << "]" << " Configuration BuildTime = " << cfg.GetBuildTime ());
  }
};

struct Engine
{

};

struct ConfigurableEngine : public Aggregator<ConfigurableEngine, Configurable, Engine, Configuration>
{};

typedef Frame<ConfigurableEngine, Motor, Configuration> ConfigurableMotorEngineFramework;

TEST_NAMESPACE_BEGIN ()
  struct InjectableFixture
  {
    InjectableFixture(void)
    {
      configurableEngine.Pack<Configurable> (new Motor);
      configurableEngine.Pack<Engine> (new Engine);
    }
    virtual
      ~InjectableFixture(void)
    {
    }
    ConfigurableEngine configurableEngine;
    ConfigurableMotorEngineFramework framework;
  };

  BOOST_FIXTURE_TEST_SUITE (, InjectableFixture)

    BOOST_AUTO_TEST_CASE (DispatchAndInject)
  {
    Configuration::SP conf1 = Configuration::Create (),
        conf2 = Configuration::Create ();
    conf1->SetBuildTime (1); conf2->SetBuildTime (2);
    // conf2 is the best fit for now, so let's use it!
    conf2->DispatchAndInject ();
    BOOST_CHECK_EQUAL (2, configurableEngine.Peek<Configuration> ()->GetBuildTime ());
    BOOST_CHECK_EQUAL (2, framework.Peek<Configuration> ()->GetBuildTime ());
    BOOST_CHECK_EQUAL (2, framework.Peek<ConfigurableEngine> ()->Peek<Configuration> ()->GetBuildTime ());
    ConfigurableEngine tmpConfigurableEngine;
    tmpConfigurableEngine.Pack<Configurable> (new Motor);
    conf1->DispatchAndInject ();
    /**
     * Only Injectables created before a DispatchAndInject () call are affected by injection!
     */
    BOOST_CHECK_EQUAL (2, configurableEngine.Peek<Configuration> ()->GetBuildTime ());
    BOOST_CHECK_EQUAL (2, framework.Peek<Configuration> ()->GetBuildTime ());
    BOOST_CHECK_EQUAL (1, tmpConfigurableEngine.Peek<Configuration> ()->GetBuildTime ());

    framework.Peek<Motor>()->Configure (framework.Ref<Configuration> ());
    framework.Peek<Motor>()->Configure (configurableEngine.Ref<Configuration> ());
    framework.Peek<Motor>()->Configure (tmpConfigurableEngine.Ref<Configuration> ());

    tmpConfigurableEngine.Peek<Configurable> ()->Configure (framework.Ref<Configuration> ());
    tmpConfigurableEngine.Peek<Configurable> ()->Configure (configurableEngine.Ref<Configuration> ());
    tmpConfigurableEngine.Peek<Configurable> ()->Configure (tmpConfigurableEngine.Ref<Configuration> ());
  }

  BOOST_AUTO_TEST_SUITE_END ()
    // InjectableFixture
TEST_NAMESPACE_END ()

